Reverse Engineering Academy's Crackme nr.1 (Splish)  www.reverser-course.de
by Elessar (elessar@runbox.com)

Howdy peeps, hows it hanging? :P
I think this is the first cracking tut i write in english, so have patience with me =)

Target: Splish.exe
Tools: Disassembler(W32Dasm), HIEW

This is the first crackme level of the REA course. Worth 15 points.

We were asked to solve three problems:
1. A hardcoded Serial
2. A simple serial-protection
3. A Nag-Screen at startup  

I chose to do the nag-screen first, so here goes:
-------------------
     Nagscreen!
-------------------
I ran the crackme and noticed the nagscreen disappearing after a few secs.
This requires some sort of timing function or loop, either by creating a timer with SetTimer,
or by some other means. I loaded the crackme into w32dasm and checked the imports.
There was no SetTimer api listed, but i did however notice the GetTickCount api.
I doubleclicked the listitem and got this:

-------------------
* Reference To: KERNEL32.GetTickCount, Ord:0158h
                                  |
:00401556 E835020000              Call 00401790
:0040155B 8945CC                  mov dword ptr [ebp-34], eax
:0040155E 8145CCD0070000          add dword ptr [ebp-34], 000007D0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040156F(U)
|

* Reference To: KERNEL32.GetTickCount, Ord:0158h
                                  |
:00401565 E826020000              Call 00401790
:0040156A 3945CC                  cmp dword ptr [ebp-34], eax
:0040156D 7602                    jbe 00401571
:0040156F EBF4                    jmp 00401565

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040156D(C)
|
:00401571 6A00                    push 00000000
:00401573 6860F00000              push 0000F060 ;SC_CLOSE, closes the dialog
:00401578 6812010000              push 00000112
:0040157D FF3511324000            push dword ptr [00403211]

* Reference To: USER32.SendMessageA, Ord:0210h
                                  |
:00401583 E8D2010000              Call 0040175A
-------------------

The GetTickCount api gets the number of milliseconds that have elapsed since windows started.
By calling it twice as we see here, we get a timer function, or rather a "loop for x milliseconds" function.
The crackme calls GetTickCount and adds 2000(7D0h) milliseconds to the result, it then loops a call to GetTickCount until
the 2 secs have elapsed. We could here change the time it is supposed to wait to decrease waitingtime before the nag closes,
or even jump over the entire gettickcount section, but i'd rather not having this code run at all. I scroll up this proc
and notice this proc gets called from 00401079:

-------------------
* Referenced by a CALL at Address:
|:00401079   <- notice this ;P
|
:00401477 55                      push ebp
:00401478 8BEC                    mov ebp, esp
:0040147A 83C4CC                  add esp, FFFFFFCC
-------------------
Let's go to that addr in w32dasm.
This is what we find:
:00401076 FF7508                  push [ebp+08]
:00401079 E8F9030000              call 00401477
:0040107E C745D030000000          mov [ebp-30], 00000030
:00401085 C745D403000000          mov [ebp-2C], 00000003

As you can see from the crackmecode this proc is called before the main window is created. The proc creates
the nagscreen and waits for 2 secs, then it returns. If we could somehow prevent this proc from being called
we defeat the nagscreen. We can do this by changing the program code. As i don't really fancy "nop cracking" i'd
rather do a jump over the call than noping it. Load the file in HIEW. Press F4 and choose decode. Scroll down
to 00401076. This is what we see:

.00401076: FF7508                       push        d,[ebp][00008]
.00401079: E8F9030000                   call       .000401477   -------- (5)
.0040107E: C745D030000000               mov         d,[ebp][-0030],000000030 ;"

Press F3 to edit .00401076, then press F2 to insert an asm instruction.
Overwrite the existing instruction with: jmps 47E then press enter. nop the next byte.
The new code should look like this:
 00000476: EB06                         jmps        00000047E                       -|
 00000478: 90                           nop					     |
 00000479: E8F9030000                   call        000000877			     |
 0000047E: C745D030000000               mov         d,[ebp][-0030],000000030 ;"     <-

Press F9 to update the file, the code flow should now bypass the nagscreen call and everything should be in order =)

.00401076: EB06                         jmps       .00040107E   -------- (5)
.00401078: 90                           nop
.00401079: E8F9030000                   call       .000401477   -------- (6)
.0040107E: C745D030000000               mov         d,[ebp][-0030],000000030 ;"

Run the exe. Wee! We're free of the demon nagscreen!
Let's get over to the next problem.

-------------------
 Hardcoded serial
-------------------
Disassemble the crackme with w32dasm, and load it for debugging. Look at the imports for known apis
for getting text from an edit box. I found the GetWindowText api. Doubleclick it a few times to loop through all
the occurences of this call. Breakpoint 'em all except the jmp. (F2). Input some bogus serial into the first edit box, 
then push the "Check Hardcoded" button. w32dasm should break on the getwindowtext call.

-------------------
* Reference To: USER32.GetWindowTextA, Ord:015Bh
                                  |
:0040136A E8BB030000              Call 0040172A <- BREAKS HERE!
:0040136F 8D0553134000            lea eax, dword ptr [00401353]
:00401375 8D1D15324000            lea ebx, dword ptr [00403215]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040138A(U)
|
:0040137B 803800                  cmp byte ptr [eax], 00
:0040137E 740C                    je 0040138C
:00401380 8A08                    mov cl, byte ptr [eax]
:00401382 8A13                    mov dl, byte ptr [ebx]
:00401384 38D1                    cmp cl, dl
:00401386 754A                    jne 004013D2
:00401388 40                      inc eax
:00401389 43                      inc ebx
:0040138A EBEF                    jmp 0040137B
-------------------
Trace past the two lea instructions, then click on the ebx and eax register buttons in the w32dasm window.
The ebx register contains a pointer to the bogus serial you entered, you can see it in the Data Display listbox.
The eax register contains a pointer to a string: "HardCoded". This is the hardcoded serial. We can see the two
strings getting compared in the above code-excerpt. Close w32dasm, run the exe and input "HardCoded". It worked =)
Too easy, an insult to our intelligence, right guys? =)
We have more to do however, the last problem, finding a working serial for your name, or in this case,
keygenning it. Let's get to work!

-------------------
 Serial-protection
-------------------
Disassemble the crackme in w32dasm. Load it up in the debugger and breakpoint the last two GetWindowText calls.
Input your name/nick into the username box, and a bogus serial into the serial box.
Push the Name/Serial button. w32dasm should break. Trace over both GetWindowText calls, but notice that the
return value from the calls are being stored in variables. This is the length of the text you entered.
The length of the serial gets stored in [00403467], and the length of the username gets stored in [00403463].

-------------------
* Reference To: USER32.GetWindowTextA, Ord:015Bh
                                  |
:0040160D E818010000              Call 0040172A <-the last getwindowtext call
:00401612 85C0                    test eax, eax
:00401614 7468                    je 0040167E
:00401616 A363344000              mov dword ptr [00403463], eax <- stores the username length here
:0040161B 33C9                    xor ecx, ecx
:0040161D 33DB                    xor ebx, ebx : used as counter
:0040161F 33D2                    xor edx, edx : used to store the remainder of a idiv operation
:00401621 8D3536324000            lea esi, dword ptr [00403236] <- points esi to your username
:00401627 8D3D58324000            lea edi, dword ptr [00403258] <- points edi to an unknown array of bytes
:0040162D B90A000000              mov ecx, 0000000A

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401650(C)
|
:00401632 0FBE041E                movsx eax, byte ptr [esi+ebx] :loads the chars from the string one by one, ebx=counter
:00401636 99                      cdq
:00401637 F7F9                    idiv ecx			:divides the ascii by 10 (0Ah)
:00401639 33D3                    xor edx, ebx			:xors the remainder with the string position
:0040163B 83C202                  add edx, 00000002		:adds 2 to edx
:0040163E 80FA0A                  cmp dl, 0A
:00401641 7C03                    jl 00401646			:checks if dl is less than 10
:00401643 80EA0A                  sub dl, 0A			:if it's not,subtract 10 from dl

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401641(C)
|
:00401646 88141F                  mov byte ptr [edi+ebx], dl	:stores dl into the array of bytes
:00401649 43                      inc ebx			:next string position
:0040164A 3B1D63344000            cmp ebx, dword ptr [00403463] :is ebx equal to username length?
:00401650 75E0                    jne 00401632			:if not get next character
-------------------

As we see here each character of the username gets divided by 10, and the remainder then gets xor'ed
with characters position within the string. It then adds 2 to edx and checks whether or not edx is
a value below 10. If it's 10 or above edx gets subtracted by 10. The results of these operations are stored
in an array of bytes at address [00403258]. In effect this code creates an array of bytes with values ranging
from 0 to 9. Let's continue with the serial calculation.

-------------------
:00401652 33C9                    xor ecx, ecx
:00401654 33DB                    xor ebx, ebx :acts as a counter
:00401656 33D2                    xor edx, edx
:00401658 8D3542324000            lea esi, dword ptr [00403242] :points esi to the serial you entered
:0040165E 8D3D4D324000            lea edi, dword ptr [0040324D] :points edi to an unknown array of bytes
:00401664 B90A000000              mov ecx, 0000000A

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040167A(C)
|
:00401669 0FBE041E                movsx eax, byte ptr [esi+ebx] :loads each character one by one,ebx=counter
:0040166D 99                      cdq
:0040166E F7F9                    idiv ecx			:divides the ascii by 10 (0Ah)
:00401670 88141F                  mov byte ptr [edi+ebx], dl	:stores remainder into the byte array
:00401673 43                      inc ebx			:next string position
:00401674 3B1D67344000            cmp ebx, dword ptr [00403467] :is ebx equal to serial length?
:0040167A 75ED                    jne 00401669			:if not get next char
:0040167C EB2A                    jmp 004016A8
-------------------
Here each character of the serial gets divided by 10, and the remainder is stored away in an array.
Let's continue.

-------------------
:004016A8 8D354D324000            lea esi, dword ptr [0040324D] :points esi to the array of bytes created from the serial
:004016AE 8D3D58324000            lea edi, dword ptr [00403258] :points edi to the array of bytes created from the username
:004016B4 33DB                    xor ebx, ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004016CB(U)
|
:004016B6 3B1D63344000            cmp ebx, dword ptr [00403463]
:004016BC 740F                    je 004016CD			:jumps if ebx=length of username, the serial is correct!
:004016BE 0FBE041F                movsx eax, byte ptr [edi+ebx] :loads one of the values into eax,ebx=counter
:004016C2 0FBE0C1E                movsx ecx, byte ptr [esi+ebx] :loads one of the values into ecx,ebx=counter
:004016C6 3BC1                    cmp eax, ecx			:compares the two values
:004016C8 7518                    jne 004016E2			:if not equal jump to "wrong serial"
:004016CA 43                      inc ebx			:next two bytes from the arrays
:004016CB EBE9                    jmp 004016B6			
-------------------

The two arrays are being compared, so we now know that the two arrays created from the username and the serial
must be equal. We now have all the information needed to keygen this crackme. Let's analyze the information.

The bytearray created from the serial is the remainders of all the characters divided by 10.
There is a similar routine for the username. The two arrays must be equal.These array of bytes contain values
ranging from 0 to 9. We need to find out how to turn the username array into a working serial.

Entering my nick, Elessar, i got this array of bytes: 01010508030404 (reversed order)
Entering a bogus serial,"123456" i got this: 090001020304 (reversed order)
"1" = ascii 31h = 49d. Divide by 10 and you get a remainder of 9. as you can see from the array.
"2" = ascii 32h = 50d. Divide by 10 and you get a remainder of 0.
"3" = ascii 33h = 51d. Divide by 10 and you get a remainder of 1.
"4" = ascii 34h = 52d. Divide by 10 and you get a remainder of 2.
"5" = ascii 35h = 53d. Divide by 10 and you get a remainder of 3.
"6" = ascii 36h = 54d. Divide by 10 and you get a remainder of 4.

By comparing the remainders of the two arrays we can start to construct the serial.
Since there are only 9 different values we get a one-to-one function between the remainders of the username array
and the numbers "1"-"9". Let's calculate the remainders of all the numbers "0"-"9", we already got "1"-"6".

"0" = ascii 30h = 48d. Divide by 10 and you get a remainder of 8. "0" - 8
"1" = ascii 31h = 49d. Divide by 10 and you get a remainder of 9. "1" - 9
"2" = ascii 32h = 50d. Divide by 10 and you get a remainder of 0. "2" - 0
"3" = ascii 33h = 51d. Divide by 10 and you get a remainder of 1. "3" - 1
"4" = ascii 34h = 52d. Divide by 10 and you get a remainder of 2. "4" - 2
"5" = ascii 35h = 53d. Divide by 10 and you get a remainder of 3. "5" - 3
"6" = ascii 36h = 54d. Divide by 10 and you get a remainder of 4. "6" - 4
"7" = ascii 37h = 55d. Divide by 10 and you get a remainder of 5. "7" - 5
"8" = ascii 38h = 56d. Divide by 10 and you get a remainder of 6. "8" - 6
"9" = ascii 39h = 57d. Divide by 10 and you get a remainder of 7. "9" - 7

Username array for Elessar: 01010508030404
Let's look it up in the table we've created.
01 = "3"
01 = "3"
05 = "7"
08 = "0"
03 = "5"
04 = "6"
04 = "6"

Serial for username Elessar = "3370566". Try it in the crackme. It works! =)
We can code a keygen by using this lookup table method.
The keygen needs to do something like this:
	* Calculate the same array from the username as the crackme does.
	* Look up the corresponding ascii number for each number in the array.

I've already coded the keygen and i have included it in this zip file.
(With sourcecode for those who are interested)

Yep, that's it folks. I hope this tutorial can help some of you guys out there.
Please mail me with comments at elessar@runbox.com.

Greets flies out to:
REA, wohoo, fantastic idea dudes! tnx =)
FHCF, norwegian cracking group
Potsmoke,trevil,f0dder,iczelion,hutch,Mix,juice,kehaven,pdrill,spiritmaster,the_viper,slicer, + everyone else i know =)
#cracking4newbies,#win32asm,#cracking.no
